package com.example.hanjyLearn.core;

import com.fasterxml.jackson.core.JsonProcessingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.transaction.TransactionTimedOutException;
import org.springframework.web.HttpMediaTypeException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 统一异常处理类
 * @author : xiect
 * @version V1.0
 * @date Date : 2021年07月19日 17:37
 **/
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 获取请求头
     * @param request
     * @return
     */
    public static Map<String, String> getHeaderMap(HttpServletRequest request) {
        Map<String, String> headerMap = new LinkedHashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            headerMap.put(name, request.getHeader(name));
        }
        return headerMap;
    }
    /**
     * 获取请求信息，包含请求参数、uri、请求头、登录用户等信息
     * @param request 手动传入请求对象
     * @return
     */
    public static String getRequestInfo(HttpServletRequest request) {
        String requestParams = null;
        try {
            requestParams = JsonUtils.toJson(request.getParameterMap());
        }
        catch (Exception e) {
            requestParams = request.getParameterMap().toString();
        }
        String bodySegment = ""; // getBodySegment(request);
//        if (bodySegment != null) {
//            bodySegment = StringUtils.format("，body：{}", bodySegment);
//        }
        Map<String, String> headerMap = getHeaderMap(request);
        return StringUtils.format(
            "请求的uri：{}，header：{}，登录用户：{}，参数：{}{}",
            request.getRequestURI(),
            headerMap,
            Optional.ofNullable((LoginUser) request.getAttribute(SecurityInterceptor.LOGIN_USER_KEY))
                .map(LoginUser::toMyJson)
                .orElse(null),
            requestParams,
            bodySegment
        );
    }

    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(HttpServletRequest request, HttpServletResponse response, Exception e, HandlerMethod handler) {
        // 默认的处理
        Result<Void> defaultResult = this.doHandleException(request, response, e, handler);
        if (handler != null) {
            Class<?> returnType = handler.getReturnType().getParameterType();
            if ((void.class.equals(returnType) || Void.class.equals(returnType))
                && !Filter.class.isAssignableFrom(handler.getBeanType())) {
                return null;
            }
        }
        String contentType = response.getContentType();
        if (response.isCommitted() && !StringUtils.isBlank(contentType)) {
            MediaType mediaType = null;
            try {
                mediaType = MediaType.parseMediaType(contentType);
            }
            catch (Throwable t) {
                // ignore
            }
            if (mediaType != null && MediaType.APPLICATION_OCTET_STREAM.includes(mediaType)) {
                // 导出时，如果请求已经提交了，那么不返回内容
                return null;
            }
        }
        return defaultResult;
    }

    private Result<Void> doHandleException(HttpServletRequest request, HttpServletResponse response, Exception e, HandlerMethod handler) {
        if (e instanceof HttpMessageNotReadableException || e instanceof HttpRequestMethodNotSupportedException || e instanceof HttpMediaTypeException) {
            log.error("请求不支持，异常原因：{}, {}", e.getMessage(), getRequestInfo(request));
            return Result.failed("请求不支持");
        }
        else if (
            "com.alibaba.nacos.api.exception.runtime.NacosDeserializationException".equals(e.getClass().getName())
                || e instanceof JsonProcessingException
                || e instanceof MethodArgumentTypeMismatchException
        ) {
            log.error("Json转换异常，异常原因：{}, {}", e.getMessage(), getRequestInfo(request), e);
            return Result.failed("请求错误");
        }
        else if (e instanceof IllegalArgumentException) {
            log.error("非法参数异常，异常原因：{}, {}", e.getMessage(), getRequestInfo(request), e);
            return Result.failed("参数非法");
        }
        else if (e instanceof BizException) {
            BizException ex = (BizException) e;
            String bizMessage = ex.getBizMessage();
            log.error("业务异常，异常原因：{}，请求信息：{}，明细信息：{}", bizMessage, getRequestInfo(request), ex.getDetail(), ex);
            return Result.failed(bizMessage);
        }
        // 检测mybatis对TransactionTimedOutException异常的包装
        Set<Throwable> throwableSet = new HashSet<>();
        Throwable current = e;
        int times = 10;
        while (current != null && --times > 0 && !throwableSet.contains(current)) {
            if (current instanceof TransactionTimedOutException) {
                log.error("事务处理超时，异常原因：{}, {}", e.getMessage(), getRequestInfo(request), e);
                return Result.failed("请求处理超时，请稍后重试");
            }
            throwableSet.add(current);
            current = current.getCause();
        }
        log.error("未知异常，异常原因：{}, {}", e.getMessage(), getRequestInfo(request), e);
        return Result.failed("系统错误");
    }

}
